home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 43 / Amiga Format CD43 (1999)(Future Publishing)(GB)(Track 1 of 2)[!][issue 1999-09].iso / -serious- / programming / other / python-1.52 / lib / python1.5 / cmd.py < prev    next >
Text File  |  1999-06-14  |  5KB  |  190 lines

  1. # A generic class to build line-oriented command interpreters
  2. #
  3. # Interpreters constructed with this class obey the following conventions:
  4. #
  5. # 1. End of file on input is processed as the command 'EOF'.
  6. # 2. A command is parsed out of each line by collecting the prefix composed
  7. #    of characters in the identchars member.
  8. # 3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method
  9. #    is passed a single argument consisting of the remainder of the line.
  10. # 4. Typing an empty line repeats the last command.  (Actually, it calls the
  11. #    method `emptyline', which may be overridden in a subclass.)
  12. # 5. There is a predefined `help' method.  Given an argument `topic', it
  13. #    calls the command `help_topic'.  With no arguments, it lists all topics
  14. #    with defined help_ functions, broken into up to three topics; documented
  15. #    commands, miscellaneous help topics, and undocumented commands.
  16. # 6. The command '?' is a synonym for `help'.  The command '!' is a synonym
  17. #    for `shell', if a do_shell method exists.
  18. #
  19. # The `default' method may be overridden to intercept commands for which there
  20. # is no do_ method.
  21. #
  22. # The data member `self.ruler' sets the character used to draw separator lines
  23. # in the help messages.  If empty, no ruler line is drawn.  It defaults to "=".
  24. #
  25. # If the value of `self.intro' is nonempty when the cmdloop method is called,
  26. # it is printed out on interpreter startup.  This value may be overridden
  27. # via an optional argument to the cmdloop() method.
  28. #
  29. # The data members `self.doc_header', `self.misc_header', and
  30. # `self.undoc_header' set the headers used for the help function's
  31. # listings of documented functions, miscellaneous topics, and undocumented
  32. # functions respectively.
  33. #
  34. # These interpreters use raw_input; thus, if the readline module is loaded,
  35. # they automatically support Emacs-like command history and editing features.
  36. #
  37.  
  38. import string
  39. import sys
  40. import linecache
  41.  
  42. PROMPT = '(Cmd) '
  43. IDENTCHARS = string.letters + string.digits + '_'
  44.  
  45. class Cmd:
  46.     prompt = PROMPT
  47.     identchars = IDENTCHARS
  48.     ruler = '='
  49.     lastcmd = ''
  50.     cmdqueue = []
  51.     intro = None
  52.     doc_leader = ""
  53.     doc_header = "Documented commands (type help <topic>):"
  54.     misc_header = "Miscellaneous help topics:"
  55.     undoc_header = "Undocumented commands:"
  56.     nohelp = "*** No help on %s"
  57.  
  58.     def __init__(self): pass
  59.  
  60.     def cmdloop(self, intro=None):
  61.         self.preloop()
  62.         if intro != None:
  63.             self.intro = intro
  64.         if self.intro:
  65.             print self.intro
  66.         stop = None
  67.         while not stop:
  68.             if self.cmdqueue:
  69.                 line = self.cmdqueue[0]
  70.                 del self.cmdqueue[0]
  71.             else:
  72.                 try:
  73.                     line = raw_input(self.prompt)
  74.                 except EOFError:
  75.                     line = 'EOF'
  76.             line = self.precmd(line)
  77.             stop = self.onecmd(line)
  78.             stop = self.postcmd(stop, line)
  79.         self.postloop()
  80.  
  81.     def precmd(self, line):
  82.         return line
  83.  
  84.     def postcmd(self, stop, line):
  85.         return stop
  86.  
  87.     def preloop(self):
  88.         pass
  89.  
  90.     def postloop(self):
  91.         pass
  92.  
  93.     def onecmd(self, line):
  94.         line = string.strip(line)
  95.         if line == '?':
  96.             line = 'help'
  97.         elif line == '!':
  98.             if hasattr(self, 'do_shell'):
  99.                 line = 'shell'
  100.             else:
  101.                 return self.default(line)
  102.         elif not line:
  103.             return self.emptyline()
  104.         self.lastcmd = line
  105.         i, n = 0, len(line)
  106.         while i < n and line[i] in self.identchars: i = i+1
  107.         cmd, arg = line[:i], string.strip(line[i:])
  108.         if cmd == '':
  109.             return self.default(line)
  110.         else:
  111.             try:
  112.                 func = getattr(self, 'do_' + cmd)
  113.             except AttributeError:
  114.                 return self.default(line)
  115.             return func(arg)
  116.  
  117.     def emptyline(self):
  118.         if self.lastcmd:
  119.             return self.onecmd(self.lastcmd)
  120.  
  121.     def default(self, line):
  122.         print '*** Unknown syntax:', line
  123.  
  124.     def do_help(self, arg):
  125.         if arg:
  126.             # XXX check arg syntax
  127.             try:
  128.                 func = getattr(self, 'help_' + arg)
  129.             except:
  130.                 try:
  131.                     doc=getattr(self, 'do_' + arg).__doc__
  132.                     if doc:
  133.                         print doc
  134.                         return
  135.                 except:
  136.                     pass
  137.                 print self.nohelp % (arg,)
  138.                 return
  139.             func()
  140.         else:
  141.             # Inheritance says we have to look in class and
  142.             # base classes; order is not important.
  143.             names = []
  144.             classes = [self.__class__]
  145.             while classes:
  146.                 aclass = classes[0]
  147.                 if aclass.__bases__:
  148.                     classes = classes + list(aclass.__bases__)
  149.                 names = names + dir(aclass)
  150.                 del classes[0]
  151.             cmds_doc = []
  152.             cmds_undoc = []
  153.             help = {}
  154.             for name in names:
  155.                 if name[:5] == 'help_':
  156.                     help[name[5:]]=1
  157.             names.sort()
  158.             # There can be duplicates if routines overridden
  159.             prevname = ''
  160.             for name in names:
  161.                 if name[:3] == 'do_':
  162.                     if name == prevname:
  163.                         continue
  164.                     prevname = name
  165.                     cmd=name[3:]
  166.                     if help.has_key(cmd):
  167.                         cmds_doc.append(cmd)
  168.                         del help[cmd]
  169.                     elif getattr(self, name).__doc__:
  170.                         cmds_doc.append(cmd)
  171.                     else:
  172.                         cmds_undoc.append(cmd)
  173.             print self.doc_leader
  174.             self.print_topics(self.doc_header,   cmds_doc,   15,80)
  175.             self.print_topics(self.misc_header,  help.keys(),15,80)
  176.             self.print_topics(self.undoc_header, cmds_undoc, 15,80)
  177.  
  178.     def print_topics(self, header, cmds, cmdlen, maxcol):
  179.         if cmds:
  180.             print header;
  181.             if self.ruler:
  182.                 print self.ruler * len(header)
  183.             (cmds_per_line,junk)=divmod(maxcol,cmdlen)
  184.             col=cmds_per_line
  185.             for cmd in cmds:
  186.                 if col==0: print
  187.                 print (("%-"+`cmdlen`+"s") % cmd),
  188.                 col = (col+1) % cmds_per_line
  189.             print "\n"
  190.